//##########################################################################
//#                                                                        #
//#                               CCLIB                                    #
//#                                                                        #
//#  This program is free software; you can redistribute it and/or modify  #
//#  it under the terms of the GNU Library General Public License as       #
//#  published by the Free Software Foundation; version 2 of the License.  #
//#                                                                        #
//#  This program is distributed in the hope that it will be useful,       #
//#  but WITHOUT ANY WARRANTY; without even the implied warranty of        #
//#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         #
//#  GNU General Public License for more details.                          #
//#                                                                        #
//#          COPYRIGHT: EDF R&D / TELECOM ParisTech (ENST-TSI)             #
//#                                                                        #
//##########################################################################
//
//*********************** Last revision of this file ***********************
//$Author::                                                                $
//$Rev::                                                                   $
//$LastChangedDate::                                                       $
//**************************************************************************
//

#ifndef SCALAR_FIELD_TOOLS
#define SCALAR_FIELD_TOOLS

#ifdef _MSC_VER
//To get rid of the really annoying warnings about template class exportation
#pragma warning( disable: 4530 )
#endif

#include "CCToolbox.h"
#include "DgmOctree.h"

#include <vector>

namespace CCLib
{

class GenericCloud;
class GenericIndexedCloud;
class GenericIndexedCloudPersist;
class GenericProgressCallback;

//! Average number of points used to compute the scalar gradient
const int NUMBER_OF_POINTS_FOR_GRADIENT_COMPUTATION = 14;

//! A K-mean class position and boundaries
struct KMeanClass
{
	//! K-mean class mean value
	DistanceType mean;
	//! K-mean class minimum value
	DistanceType minValue;
	//! K-mean class maximum value
	DistanceType maxValue;
};

//! Severeal scalar field treatment algorithms (gradient, classification, etc.)
/** This toolbox provides several algorithms to apply
	treatments and handle scalar fields
**/
#ifdef CC_USE_AS_DLL
#include "CloudCompareDll.h"
class CC_DLL_API ScalarFieldTools : public CCToolbox
#else
class ScalarFieldTools : public CCToolbox
#endif
{
public:

	//! Computes the geometrical gradient of a scalar field associated to a point cloud
	/** See Daniel Girardeau-Montaut's PhD manuscript (Chapter 3, section 3.3.2) for more
		information. As explained in this section, if the scalar field corresponds to
		(euclidian) distances between the points and another entity, then it is possible
		to filter out aberrant values.
		\param theCloud a point cloud (associated to scalar values)
		\param signedSF specifies whether the input scalar field is signed or not
		\param euclidianDistances indicates if the scalar values are euclidian distances
		\param sameInAndOutScalarField specifies that the 'in' and 'out' scalar field of the input point cloud are the same structure
		\param progressCb the client application can get some notification of the process progress through this callback mechanism (see GenericProgressCallback)
		\param theOctree the octree, if it has already been computed
		\return error code (0 if ok)
	**/
	static int computeScalarFieldGradient(GenericIndexedCloudPersist* theCloud, 
											bool signedSF, 
											bool euclidianDistances, 
											bool sameInAndOutScalarField,
											GenericProgressCallback* progressCb=0, 
											DgmOctree* theOctree=0);

	//! Computes a spatial gaussian filter on a scalar field associated to a point cloud
	/** The "amplitutde" of the gaussian filter must be precised (sigma).
		As 99% of the gaussian distribution is between -3*sigma and +3*sigma
		around the mean value, this filter will only look for neighbouring
		points (around each point) in a sphere of radius 3*sigma.
        It also permits to use the filter as a bilateral filter. Where the wights are computed also considering the
        distance of the neighbor's scalar value from the current point scalar value. (weighted with gaussian as distances are)
		Warning: this method assumes the input scalar field is different from output.
		\param sigma filter variance
		\param theCloud a point cloud (associated to scalar values)
		\param signedSF specifies whether the input scalar field is signed or not
        \param sigmaSF the sigma for the bilateral filter. when different than -1 turns the gaussian filter into a bilateral filter
		\param progressCb the client application can get some notification of the process progress through this callback mechanism (see GenericProgressCallback)
		\param theOctree the octree, if it has already been computed
		\return success
	**/
	static bool applyScalarFieldGaussianFilter(float sigma, 
												GenericIndexedCloudPersist* theCloud, 
                                                bool signedSF,
                                                float sigmaSF,
												GenericProgressCallback* progressCb=0, 
												DgmOctree* theOctree=0);

	//! Multiplies two scalar fields of the same size
	/** The first scalar field is updated (S1 = S1*S2).
		\param firstCloud the first point cloud (associated to scalar values)
		\param secondCloud the second point cloud (associated to scalar values)
		\param progressCb the client application can get some notification of the process progress through this callback mechanism (see GenericProgressCallback)
	**/
	static void multiplyScalarFields(GenericIndexedCloud* firstCloud, 
										GenericIndexedCloud* secondCloud, 
										GenericProgressCallback* progressCb=0);

	//! Computes an histogram of the scalar field with a given number of classes
	/** The scalar values are projected in a given number of classes,
		regularily spaced between the lowest and the highest value of
		the scalar field.
		\param theCloud a point cloud (associated to scalar values)
		\param numberOfClasses number of histogram classes
		\param histo number of elements per histogram class
		\param includeNegValues specifies whether negative values should be included in computation
	**/
	static void computeScalarFieldHistogram(const GenericCloud* theCloud, 
											unsigned numberOfClasses, 
											std::vector<int>& histo, 
											bool includeNegValues);

	//! Compute the extreme values of a scalar field
	/** \param theCloud a point cloud, with a scalar field activated
		\param minV a field to store the minimum value
		\param maxV a field to store the maximum value
		\param includeNegValues specifies whether negative values should be included in computation
	**/
	static void computeScalarFieldExtremas(const GenericCloud* theCloud, 
											DistanceType& minV, 
											DistanceType& maxV, 
											bool includeNegValues);

	//! Count the number of positive values in a scalar field
	/** \param theCloud a point cloud, with a scalar field activated
	**/
	static unsigned countScalarFieldPositiveValues(const GenericCloud* theCloud);

	//! Classifies automaticaly a scalar field in K classes with the K-means algorithm
	/** The initial K classes positions are regularily spaced between the
		lowest and the highest values of the scalar field. Eventually the
		algorithm will converge and produce K classes.
		\param theCloud a point cloud (associated to scalar values)
		\param K the number of classes
		\param kmcc an array of size K which will be filled with the computed classes limits (see ScalarFieldTools::KmeanClass)
		\param includeNegValues specifies whether negative values should be included in computation
		\param progressCb the client application can get some notification of the process progress through this callback mechanism (see GenericProgressCallback)
	**/
	static bool computeKmeans(const GenericCloud* theCloud, 
								uchar K, 
								KMeanClass kmcc[], 
								bool includeNegValues, 
								GenericProgressCallback* progressCb=0);

	//! Sets the distance value associated to a point to the default "HIDDEN" value (-1)
	/** Generic function that can be used with the GenericCloud::foreach() method.
        Warning: only valid for strictly positive scalar fields.
		\param aPoint a 3D point
		\param aScalarValue its associated scalar value
	**/
	static void razDistsToHiddenValue(const CCVector3& aPoint, DistanceType& aScalarValue);

	//! Sets the distance value associated to a point to the biggest acceptable value (BIG_VALUE)
	/** Generic function that can be used with the GenericCloud::foreach() method.
        This method is meant to be used with non strictly positive scalar fields.
        Prefer razDistsToOutValue otherwise.
		\param aPoint a 3D point
		\param aScalarValue its associated scalar value
	**/
	static void razDistsToBigValue(const CCVector3& aPoint, DistanceType& aScalarValue);

	//! Sets the distance value associated to a point to zero
	/** Generic function that can be used with the GenericCloud::foreach() method.
		\param aPoint a 3D point
		\param aScalarValue its associated scalar value
	**/
	static void razDistsToZero(const CCVector3 &aPoint, DistanceType& aScalarValue);

protected:

	//! "Cellular" function to compute the gradient norms of points inside an octree cell
	/** This function is meant to be applied to all cells of the octree
		(it is of the form DgmOctree::localFunctionPtr).
		See ScalarFieldTools::computeScalarFieldGradient.
		Method parameters (defined in "additionalParameters") are :
		- (GenericCloud*) the point cloud
		- (bool*) specifies if the scalar values are euclidian distances
		- (float*) radius
		- (std::vector<DistanceType>*) the gradient norms container
		\param cell structure describing the cell on which processing is applied
		\param additionalParameters see method description
	**/
	static bool computeMeanGradientOnPatch(const DgmOctree::octreeCell& cell, void** additionalParameters);

	//! "Cellular" function to apply a gaussian filter on the scalar values of points inside an octree cell
	/** This function is meant to be applied to all cells of the octree
        The method also permits to use a bilateral behaviour for the filter. This is automatically switched on
        if its sigmaSF parameter in additionalParameters is different than -1
		(it is of the form DgmOctree::localFunctionPtr).
		See ScalarFieldTools::applyScalarFieldGaussianFilter.
		Method parameters (defined in "additionalParameters") are :
		- (float*) sigma
		- (std::vector<DistanceType>*) the smoothed values
		\param cell structure describing the cell on which processing is applied
		\param additionalParameters see method description
	**/
	static bool computeCellGaussianFilter(const DgmOctree::octreeCell& cell, void** additionalParameters);

};

}

#endif
